package org.rcy.framework.data.transaction;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 吴康桥
 * @Description
 * @date 2022/9/28 9:22
 */


public class RcyDataSourceContext implements BaseDataSourceContext{

	private final static Logger logger = LoggerFactory.getLogger(RcyDataSourceContext.class);

	private static final Map<String, TransactionInfo> transactionMap = new ConcurrentHashMap<>();

	private long maxTransactionTime = 30000;

	private int maxLimit; // 最大返回行数

	private int maxLogTime = 8000; // sql执行maxLogTime时间（单位毫秒）打印出时间，需在设置maxLimit情况下该设置起效

	private boolean monitTransaction;

	private long timerPeriod = 30000L;

	public RcyDataSourceContext(long maxTransactionTime, int maxLimit, int maxLogTime) {
		super();
		this.maxTransactionTime = maxTransactionTime;
		this.maxLimit = maxLimit;
		if(maxLogTime > 0) {
			this.maxLogTime = maxLogTime;
		}
	}

	public TransactionInfo addTransactionInfo(String method, long startTime, String traceId, String connectionId) {
		if(connectionId == null || "".equals(connectionId)) {
			return null;
		}
		TransactionInfo transactionInfo = null;
		try {
			transactionInfo = new TransactionInfo(method, startTime, traceId, connectionId);
			if(monitTransaction) {
				transactionMap.put(connectionId, transactionInfo);
			}
		} catch (Exception e) {
			logger.error("addTransactionInfo key {} traceId {} error {}", connectionId, traceId, e.getMessage(), e);
		}
		return transactionInfo;
	}

	public void removeTransactionInfo(String key) {
		if(key == null || "".equals(key)) {
			return;
		}
		try {
			if(monitTransaction) {
				transactionMap.remove(key);
			}
		} catch (Exception e) {
			logger.error("removeTransactionInfo key {} error {}", key, e.getMessage(), e);
		}
	}

	@Override
	public String getTransactionInfo() {
		Collection<TransactionInfo> infos = transactionMap.values();
		long currentTime = System.currentTimeMillis();
		StringBuilder buffer = new StringBuilder();
		buffer.append("[");
		for(TransactionInfo info:infos) {
			buffer.append("{")
					.append("\"method\":\"").append(info.getMethod()).append("\",")
					.append("\"traceId\":\"").append(info.getTractId()).append("\",")
					.append("\"connectionId\":").append(info.getConnectionId()).append(",")
					.append("\"cost\":").append(info.cost(currentTime))
					.append("}").append(",");
		}
		if(infos.isEmpty()) {
			buffer.replace(buffer.lastIndexOf(","), buffer.length(), "");
		}
		buffer.append("]");
		return buffer.toString();
	}

	@Override
	public int getTransactionSize() {
		return transactionMap.size();
	}

	@Override
	public void clearTransaction() {
		transactionMap.clear();
	}

	@Override
	public long getMaxTransactionTime() {
		return maxTransactionTime;
	}

	@Override
	public void setMaxTransactionTime(long maxTransactionTime) {
		this.maxTransactionTime = maxTransactionTime;
	}

	@Override
	public int getMaxLimit() {
		return maxLimit;
	}

	@Override
	public void setMaxLimit(int maxLimit) {
		this.maxLimit = maxLimit;
	}

	@Override
	public int getMaxLogTime() {
		return maxLogTime;
	}

	@Override
	public void setMaxLogTime(int maxLogTime) {
		this.maxLogTime = maxLogTime;
	}

	@Override
	public boolean isMonitTransaction() {
		return monitTransaction;
	}

	@Override
	public void setMonitTransaction(boolean monitTransaction) {
		this.monitTransaction = monitTransaction;
	}



	public void startSchedule() {
		Timer timer = new Timer();
		timer.schedule(new TimerTask() {
			@Override
			public void run() {
				if(!monitTransaction && transactionMap.size() == 0) {
					return;
				}
				logger.debug("transaction schedule run transactionMap size {} monit {}", transactionMap.size(),monitTransaction);
				try {
					Iterator<Map.Entry<String, TransactionInfo>> entries = transactionMap.entrySet().iterator();
					long currentTime = System.currentTimeMillis();
					while (entries.hasNext()) {
						Map.Entry<String, TransactionInfo> entry = entries.next();
						TransactionInfo info = entry.getValue();
						String key = entry.getKey();
						long cost = info.cost(currentTime);
						if (cost > maxTransactionTime) {
							logger.warn("transaction schedule {} connectionId {} tractId {} cost {}ms ", info.getMethod(), info.getConnectionId(), info.getTractId(), cost);
						}
						if (cost > 5 * 60 * 1000) {
							logger.warn("transaction schedule removed {} connectionId {} tractId {} cost {}ms ", info.getMethod(), info.getConnectionId(), info.getTractId(), cost);
							transactionMap.remove(key);
						}
					}
				} catch (Exception e) {
					logger.error("Transaction schedule error {}", e.getMessage(), e);
				}
			}
		}, maxTransactionTime, timerPeriod);
	}

	public void handleMBeans() {
		try {
			final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
			final ObjectName beanConfigName = new ObjectName(BaseDataSourceContext.objectNameStr);
			if (!mBeanServer.isRegistered(beanConfigName)) {
				mBeanServer.registerMBean(this, beanConfigName);
				logger.info("JMX handleMBeans register {}", BaseDataSourceContext.objectNameStr);
			} else {
				logger.error("JMX handleMBeans has registered {}", BaseDataSourceContext.objectNameStr);
			}
		} catch (Exception e) {
			logger.error("JMX handleMBeans error {}",e.getMessage(),e);
		}
	}
}
